/*
--------------------------------------------------------------------------------
				Ak-Lib. 
			 Project       	: Ak-Lib
			 Group         	: Ak-Lib R&D Team
			 Author        	: Ocean.Wang
			 Create Date 	: 05-01-2010
--------------------------------------------------------------------------------
Code Name 	:  A.K 	~v~
Description	:  Yet Another Cross-Platform Lib.
Cautions    	:
--------------------------------------------------------------------------------
Change History:
Version		: 0.1
Building 		: 05/01/10
10-05.01 first draft, build, debug and test.
--------------------------------------------------------------------------------
*/
#include "ak_clk.h"
#include "ak_sbf.h"

static 	int _gTraceLvl_SBF = TRACELVL_WARNING;

#define SBF_TASK_STACK 		0X100000
#define SBF_TASK_PRI 		90

#define SBF_TICK_RES 		50
#define SBF_TICK_MAX 		((LONG_MAX-1)/2)
#define SBF_TICK_JR 		(SBF_TICK_MAX/2)

#define SBF_TICK_LAZY 		6

static 	__inline 	unsigned long 	_SBF_GetCasId(_t_SBF_CcCtx * pCcCtx)
{
	return ATOMIC_INC(&(pCcCtx->casId));
}

/* ------------------------------------------------------------------
**	SM Back Fill Cb Pool
**	
*/
static	int 		_SBF_CbPool_Incr(_t_SBF_CbPool * pPool, int count)
{
	int 			i;
	unsigned char 	* pMem;
	_t_sysMi		* pNode;
	
	pMem	= (SYS_MALLOC(count * sizeof(_t_SBF_CbNode)));
	if(pMem){
		pPool->incrCnt += 1;
		for(i=0; i<count; i++, 	pMem += sizeof(_t_SBF_CbNode)){
						
			pNode 			= (_t_sysMi *)pMem;
			pNode->jar 		= pPool;
			pNode->type  	= pPool->type;
			BLST_ADD2T(&(pPool->queue), &(pNode->aNode));
		}
		return 0;
	}
	return -1;
}

static	__inline _t_SBF_CbNode*	_SBF_CbPool_Alloc(_t_SBF_CbPool * pPool){
	_t_bnode 	* pNode;
	_t_SBF_CbNode * pCbNode;

	pCbNode = NULL;
	if(pPool){
		SYS_ENTER_CS(&(pPool->lock));
		if(pPool->queue.count == 0){
			if(pPool->incrCnt <= pPool->incrMax)
				_SBF_CbPool_Incr(pPool, pPool->incrSz);
		}
		pNode = BLST_GET(&(pPool->queue));
		if(pNode){
			pCbNode = _O_PARENT(_t_SBF_CbNode, ci.mi.aNode, pNode);
		}
		SYS_LEAVE_CS(&(pPool->lock));
	}
	return pCbNode;
}

static	__inline	void		_SBF_CbPool_Free(_t_SBF_CbPool * pPool, _t_SBF_CbNode * pCbNode){
	if(pCbNode && pPool){
		SYS_ENTER_CS(&(pPool->lock));
		BLST_ADD2H(&(pPool->queue), &(pCbNode->ci.mi.aNode));
		SYS_LEAVE_CS(&(pPool->lock));
	}
}

static	void	_SBF_CbPool_Init(_t_SBF_CbPool * pPool, int incrMax, int incrSz, int initSz, int type)
{
	pPool->incrCnt = 0;
	pPool->incrMax = incrMax;
	pPool->incrSz  = incrSz;
	pPool->type = type;
	SYS_INIT_CS(&((pPool)->lock));
	BLIST_INIT(&((pPool)->queue));
	
	_SBF_CbPool_Incr((pPool), initSz);
	return; 
}



/* ------------------------------------------------------------------
**	Clt CbTree : KTree (for Key-Value Node)
*/
static 	int 	_SBF_CbTreeK_Cmp(_t_rbnode * node1, _t_rbnode * node2)
{
	unsigned long key1;
	unsigned long key2;
	
	key1 = (_O_PARENT(_t_sysLruCi, mi.kNode, node1))->casId;
	key2 = (_O_PARENT(_t_sysLruCi, mi.kNode, node2))->casId;

	if(key1 > key2){
		return _TREE_GREAT;
	}
	else if(key1 < key2){
		return _TREE_LESS;
	}
	
	return _TREE_EQUAL;
}


/* ------------------------------------------------------------------
** Clt Expire: 
*/
static 	int 	_SBF_CbFree(_t_SBF_CbTree * pCbTree, _t_SBF_CbNode * pExp)
{
	unsigned int 	type;

	if(pCbTree && pExp){
		type	= pExp->ci.ti.type & SBF_CB_A_MASK;
		if( (type == SBF_CB_SYNC) && (pExp->pWake && pExp->outBuf && pExp->outLen)){
			*(pExp->outLen) = 0;
			SYS_SEM_B_GIVE(pExp->pWake);
			pExp->pWake = NULL;
		}
		/* Allocator */
		_SBF_CbPool_Free(pExp->ci.mi.jar, pExp);
	}

	return 0;
}

static 	int 	_SBF_CbExpireQ(_t_SBF_CbTree * pCbTree, _t_blist * pQue)
{
	unsigned int 	type;
	_t_SBF_CbNode*	pExp;
	_t_bnode 	 * 	pNode;

	while((pNode=BLST_GET(pQue))){
		pExp = _O_PARENT(_t_SBF_CbNode, ci.mi.aNode, pNode);
		type	= pExp->ci.ti.type & SBF_CB_A_MASK;
		if( (type == SBF_CB_SYNC) && (pExp->pWake && pExp->outBuf && pExp->outLen)){
			*(pExp->outLen) = 0;
			SYS_SEM_B_GIVE(pExp->pWake);
			pExp->pWake = NULL;
		}
		/* Allocator */
		_SBF_CbPool_Free(pExp->ci.mi.jar, pExp);
	}

	return 0;
}

/* ------------------------------------------------------------------
**	SBF Trigger: 50ms Based.
*/
static 	int _SBF_CcExpire(_t_SBF_CcCtx * pCcCtx)
{
	_t_SBF_CbTree * pCbTree;
	time_t 			expire;
	int 			flag;
	unsigned int 	cTree;

	STask_Attr(&(pCcCtx->tAttr));
	STask_SetName("SBF_CcExpire");
	
	while(1){
		SYS_SEM_B_TAKE(&(pCcCtx->gcWake), _WAIT_FOREVER);

		/* Step */
		pCcCtx->current = SLru_TickStep(pCcCtx->current);

		/* Expire */
		expire 	= pCcCtx->current;
		for(cTree=0; cTree<SBF_TREE_MAX; cTree++){
			pCbTree = &(pCcCtx->cbTree[cTree]);
			flag 	= SLru_Expire(&(pCbTree->ct), expire, SBF_TICK_LAZY);
		}
	}
	return 0;
}

int 	SBF_CcTrigger(_t_SBF_CcCtx * pCcCtx)
{
	if(pCcCtx){
		SYS_SEM_B_GIVE(&(pCcCtx->gcWake));
	}
	return 0;
}


static 	int 	_SBF_CbEntry(_t_SBF_CbTree * pCbTree, _t_SBF_CbNode * pCbNode, void * pMsg, int lMsg)
{
	int 		type, length;
	_t_cbentry  * 	pCbEntry;

	type = pCbNode->ci.ti.type & SBF_CB_A_MASK;
	switch(type){
		case SBF_CB_ASYNC:
			pCbEntry = &(pCbNode->cbEntry);
			if(pCbEntry->cbProc){
				pCbEntry->cbProc(pCbEntry->pArg, 
								pCbEntry->lArg, 
								pMsg, lMsg);
			}
			break;
		case SBF_CB_SYNC:
			if(pCbNode->pWake && pCbNode->outBuf && pCbNode->outLen){
				length 	= *(pCbNode->outLen);
				if(pMsg && (lMsg > 0) && (lMsg <= length)){
					SYS_MEMCPY(pCbNode->outBuf, pMsg, lMsg);
					*(pCbNode->outLen) = lMsg;
					SYS_SEM_B_GIVE(pCbNode->pWake);					
				}else{
					*(pCbNode->outLen) = 0;
					SYS_SEM_B_GIVE(pCbNode->pWake);
				}
				pCbNode->pWake = NULL;
					
			}
			break;

		default:
			break;
	}
	
	/* Erase the CbNode */
	_SBF_CbPool_Free(pCbNode->ci.mi.jar, pCbNode);
	return 0;
	
}

int 		SBF_CcCb(_t_SBF_CcCtx * 	pCcCtx, 	
					long 			lArg, 
					void * 			pMsg, 	
					int 			lMsg,
					unsigned long 	casId)
{
	_t_SBF_CbNode  	* 	pCbNode;
	_t_SBF_CbTree  	* 	pCbTree;
	_t_sysLruCi			lruCi;
	_t_sysLruCi		*	pLruCi;
	
	if(pCcCtx){
		pCbTree = &(pCcCtx->cbTree[casId%SBF_TREE_MAX]);
		/* Find the CbNode */
		lruCi.casId = casId;
		pLruCi = SLru_Del(&(pCbTree->ct), &lruCi); 
		if(pLruCi){
			#if 1
			SYS_TRACE(_gTraceLvl_SBF, TRACELVL_DEBUG, "CC[%u]: (CAS:%lu) {%p}\n", 
				pCbTree->ct.index,	casId,	pLruCi);
			#endif
			pCbNode = _O_PARENT(_t_SBF_CbNode, ci, pLruCi);
			_SBF_CbEntry(pCbTree, pCbNode, pMsg, lMsg);
		}
	}
	return 0;
}

/* =====================================================================
**	 Client Interface :
*/
#define SBF_CB_ASYNC_INIT_CNT	0X10000
#define SBF_CB_ASYNC_INCR_MAX 	0X04
#define SBF_CB_ASYNC_INCR_SZ 	0X2000

#define SBF_CB_SYNC_INIT_CNT	0X400
#define SBF_CB_SYNC_INCR_MAX 	0X04
#define SBF_CB_SYNC_INCR_SZ 	0X100

int 	SBF_Init(_t_SBF_CcCtx * pCcCtx)
{
	_t_SBF_CbTree 	* 	pCbTree;
	_t_cbentry 			cbEntry;
	int 				i;

	if(pCcCtx){
		_SBF_CbPool_Init(&(pCcCtx->poolSync),
						SBF_CB_SYNC_INCR_MAX, 
						SBF_CB_SYNC_INCR_SZ, 
						SBF_CB_SYNC_INIT_CNT,
						SBF_CB_SYNC);
	
		_SBF_CbPool_Init(&(pCcCtx->poolASync),
						SBF_CB_ASYNC_INCR_MAX, 
						SBF_CB_ASYNC_INCR_SZ, 
						SBF_CB_ASYNC_INIT_CNT,
						SBF_CB_ASYNC);

		_KLFUNC_INIT(&(pCcCtx->klFunc), 
				_SBF_CbTreeK_Cmp, 
				NULL,	NULL,
				_SBF_CbFree, 	_SBF_CbExpireQ);
		/* Init the CbTree */
		pCbTree = pCcCtx->cbTree;
		for(i=0; i<SBF_TREE_MAX; i++, pCbTree++){
			_SLRU_CT_INIT(&(pCbTree->ct), 
					NULL, &(pCcCtx->klFunc), 
					i, 
					&(pCcCtx->current));
			pCbTree->pCcCtx= pCcCtx;
			pCbTree->pSync 	= &(pCcCtx->poolSync);
			pCbTree->pASync = &(pCcCtx->poolASync);
			
		}

		/* Current Tick */
		pCcCtx->current = 0;

		/* Init the Task */
		SYS_SEM_B_INIT(&(pCcCtx->gcWake));
		STASK_CRATE(pCcCtx->thread, SBF_TASK_STACK, SBF_TASK_PRI, _SBF_CcExpire, pCcCtx);

		/* Add the Lru Trigger : 5ms Res ? */
		_CBENTRY_INIT(&cbEntry, SBF_CcTrigger, pCcCtx, 0);
		SClk_Add(&cbEntry);
		
		return 0;
	}

	return -1;
}


/* =====================================================================
** Operations:
*/
_t_SBF_CbNode * 	SBF_Apply(_t_SBF_CcCtx * pCcCtx, _t_cbentry * pCbEntry, int timeout)
{
	_t_SBF_CbNode	* 	pCbNode;
	_t_SBF_CbTree 	* 	pCbTree;
	
	unsigned long 		casId;
	
	if(pCcCtx && pCbEntry &&(pCbEntry->cbProc)){
		pCbNode = _SBF_CbPool_Alloc(&(pCcCtx->poolASync));
		if(pCbNode){
			_CBENTRY_ASSIGN(&(pCbNode->cbEntry), pCbEntry);
			casId = _SBF_GetCasId(pCcCtx);

			/* Add 2 CallBack */
			if(timeout < 0){
				timeout = 100;
			}
			pCbNode->ci.casId  		= casId;
			pCbNode->ci.ti.expire 	= timeout;
			pCbTree = &(pCcCtx->cbTree[casId%SBF_TREE_MAX]);
			SLru_Add(&(pCbTree->ct), &(pCbNode->ci));

			return pCbNode;
		}
	}

	return NULL;
}


_t_SBF_CbNode *	SBF_ApplyEx(_t_SBF_CcCtx *	pCcCtx, unsigned char *	outBuf, int * outLen, int timeout)
{
	_t_SBF_CbNode	* 	pCbNode;
	_t_SBF_CbTree 	* 	pCbTree;
	
	_t_sysSemB 			opWake;
	
	unsigned long 		casId;
	
	if(	pCcCtx &&outBuf && outLen && (*outLen > 0)){
		pCbNode = _SBF_CbPool_Alloc(&(pCcCtx->poolSync));
		if(pCbNode){
			casId = _SBF_GetCasId(pCcCtx);
			/* Add 2 CallBack */
			if(timeout < 0){
				timeout = 100;
			}
			
			SYS_SEM_B_INIT(&opWake);
			pCbNode->outBuf = outBuf;
			pCbNode->outLen = outLen;
			pCbNode->pWake  = &opWake;

			pCbNode->ci.casId  		= casId;
			pCbNode->ci.ti.expire 	= timeout;
			pCbTree = &(pCcCtx->cbTree[casId%SBF_TREE_MAX]);
			SLru_Add(&(pCbTree->ct), &(pCbNode->ci));
			
			/* Now Let's Wait */
			#if 0
			SYS_SEM_B_TAKE(&opWake, _WAIT_FOREVER);
			#endif
			/* When WakeUp, OutBuf & OutLen is Already Set */

			return pCbNode;
		}
	}

	return NULL;
}



